
require 'pp'
require 'cgi'

class CornoStyle
  def self.to_html(text, plugin = nil)
    doc = CornoStyle.new(text, plugin)

    doc.parse_style

    doc.compile_blockquote
    doc.compile_super_pre
    doc.compile_title
    doc.compile_horizontal_rule
    doc.compile_plugin

#   pp doc.tokens

    doc.compile_token
  end

  attr_accessor :text, :tokens

  def initialize(text, plugin)
    @text = text.gsub(/(\r\n)|(\r)/, "\n")
    @tokens = []
    @plugin = plugin
  end

  def style
    'CornoStyle'
  end

  STYLES =[ 
    {:type => :blockquote, :regexp => /^>>\n(.+?)<<\n?/ms},
    {:type => :super_pre, :regexp => /^>\|\|\n(.+?)\|\|<\n?/ms},

#   {:type => :table, :regexp => //ms},
#   {:type => :list, :regexp => //ms},
#   {:type => :dlist, :regexp => //ms},

    {:type => :title, :regexp => /^(\*{2,4}) (.+?)\n/ms},
    {:type => :horizontal_rule, :regexp => /^-{4}\n/ms},
    {:type => :plugin, :regexp => /\{\{(.+?)\}\}/ms},
  ]

  def parse_style
    tokens = [
      {:type => '', :contents => @text},
    ]

    STYLES.each do |style|
      part_tokens = []

      tokens.each do |token|
        t = token[:contents]

        while style[:regexp].match(t)
          part_tokens << {:type => 'paragraph', :contents => $`}  unless $`.empty?
          part_tokens << {:type => style[:type], :contents => $&}

          t = $'
        end  if token[:type] != :blockquote && token[:type] != :super_pre

#       part_tokens << {:type => token[:type], :contents => t}  unless t.nil?
        part_tokens << {:type => token[:type], :contents => t}  unless t.empty?

      end

      tokens = part_tokens
    end

    @tokens = tokens
  end

  def compile_token
    s = []

    @tokens.each do |token|
      s << token[:contents]
    end

    s.join('')
  end

  def compile_blockquote
    style = STYLES.find {|s| s[:type] == :blockquote }
    return false  if style.nil?

    @tokens.each do |token|
      if token[:type] == :blockquote
        token[:contents] = token[:contents].gsub(style[:regexp]) do
          text = $1

          text = text.gsub(/(^\n)|(\n$)/ms, '')
          text = CGI.escapeHTML(text)
          text = text.gsub(/^(\s+)/ms) { '&nbsp;' * $1.length }

          "<blockquote>\n#{text}\n</blockquote>\n"
        end
      end
    end

    true
  end

  def compile_super_pre
    style = STYLES.find {|s| s[:type] == :super_pre }
    return false  if style.nil?

    @tokens.each do |token|
      if token[:type] == :super_pre
        token[:contents] = token[:contents].gsub(style[:regexp]) do
          text = $1

          text = CGI.escapeHTML(text)
          text = text.gsub(/\A(\s+)/ms) { '&nbsp;' * $1.length }

          "<pre>#{text}</pre>\n"
        end
      end
    end

    true
  end

  def compile_title
    style = STYLES.find {|s| s[:type] == :title }
    return false  if style.nil?

    @tokens.each do |token|
      if token[:type] == :title
        token[:contents] = token[:contents].gsub(style[:regexp]) do
          num = $1.length + 1
          text = $2

          text = CGI.escapeHTML(text)

          "<h#{num}>#{text}</h#{num}>\n"
        end
      end
    end

    true
  end

  def compile_horizontal_rule
    style = STYLES.find {|s| s[:type] == :horizontal_rule }
    return false  if style.nil?

    @tokens.each do |token|
      if token[:type] == :horizontal_rule
        token[:contents] = token[:contents].gsub(style[:regexp]) do
          "<hr />\n"
        end
      end
    end

    true
  end

  def compile_plugin
    style = STYLES.find {|s| s[:type] == :plugin }
    return false  if style.nil?
    return false  if @plugin.nil?

    @tokens.each do |token|
      if token[:type] == :plugin
        token[:contents] = token[:contents].gsub(style[:regexp]) do |s|
          begin
            res = ''
            @plugin.instance_eval { res = eval($1) }
            res
          rescue Exception => e
            puts e.message
            s
          end
        end
      end
    end

    true
  end
end

if $0 == __FILE__

  text = <<SAMPLE_TEXT

title
comment

>||
class Foo
  def bar'baz' # return baz
{{plugin1 ("argv1")}}
  end
end
||<

----

>>
abcdefghij
0123456789
<<

bar>>
foo
<<

http://d.hatena.ne.jp/gtaka555/

aaa

{{plugin1 ("argv1")}}

gggg

SAMPLE_TEXT

  puts CornoStyle::to_html(text)
end

